/*
 * The MIT License
 *
 * Copyright 2012 http://generic-toolset.googlecode.com.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.rlpw.swing;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Comparator;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.event.EventListenerList;

/**
 *
 * @param <T> the type of the elements
 */
public class JORadioGroup<T> extends JPanel implements View<T>, Selectable<T> {

    private Model<T> model;
    private SelectModel<T> selected;
    private FilteredList<T> flist;
    private EventListenerList listeners = new EventListenerList();

    public JORadioGroup() {
        this(new DefaultModel<T>(), null);
    }

    public JORadioGroup(String title) {
        this(new DefaultModel<T>(), title);
    }

    public JORadioGroup(Model<T> model) {
        this(model, null);
    }

    public JORadioGroup(Model<T> model, String title) {
        if (title != null) {
            setBorder(BorderFactory.createTitledBorder(title));
        }
        setDataModel(model);
        flist = new FilteredList<>(this);
        selected = new DefaultSelectModel<>(this);
        selected.setMode(SelectModel.SINGLE);
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        updateView();
    }

    /** {@inheritDoc} */
    @Override
    public void updateView() {

        if (flist != null) {
            flist.update();
        }

        if (getSelectModel() == null) {
            return;
        }

        removeAll();

        for (final T item : flist) {
            final JRadioButton cb = new JRadioButton(item.toString(), getSelectModel().isSelected(item));
            cb.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    getSelectModel().setSelected(item, cb.isSelected());
                    fireActionEvent();
                }
            });
            cb.setFocusPainted(false);
            add(cb);
        }
        revalidate();
        repaint();
    }

    /** {@inheritDoc} */
    @Override
    public Model<T> getDataModel() {
        return model;
    }

    /** {@inheritDoc} */
    @Override
    public void setDataModel(Model<T> model) {
        if (model == null) {
            throw new IllegalArgumentException("Cannot set a null Model");
        }
        if (this.model != null) {
            this.model.removeView(this);
        }
        model.addView(this);
        this.model = model;
        updateView();
    }


    /** {@inheritDoc} */
    @Override
    public SelectModel<T> getSelectModel() {
        return selected;
    }

    /** {@inheritDoc} */
    @Override
    public Filter<T> getFilter() {
        return flist.getFilter();
    }

    /** {@inheritDoc} */
    @Override
    public void setFilter(Filter<T> filter) {
        flist.setFilter(filter);
    }

    /** {@inheritDoc} */
    @Override
    public void setSorter(Comparator<T> comp) {
        flist.setSorter(comp);
    }

    /** {@inheritDoc} */
    @Override
    public Comparator<T> getSorter() {
        return flist.getSorter();
    }

    /** {@inheritDoc} */
    @Override
    public List<T> getVisibleItems() {
        return flist;
    }

    /**
     * Returns the current selected Item. 
     * <br/>
     * This method uses the {@code SelectModel}:
     * 
     * <pre>
     * <code>
     * getSelectModel().getSelected().get(0)
     * </code>
     * </pre>
     *  
     * @return the current selected Item or {@code null}
     * @see org.rlpw.swing.SelectModel#getSelected
     * @see #setSelectedItem
     */
    public T getSelectedItem() {
        if (getSelectModel().countSelected() > 0) {
            return getSelectModel().getSelected().get(0);
        } else {
            return null;
        }
    }

    /**
     * Selects the specified Item from the {@code JORadioGroup}. 
     * <br/>
     * This method uses the {@code SelectModel}:
     * 
     * <pre>
     * <code>
     * getSelectModel().setSelected(item, true)
     * </code>
     * </pre>
     * 
     * @param item the element to select
     * @see org.rlpw.swing.SelectModel#setSelected
     * @see #getSelectedItem
     */
    public void setSelectedItem(T item) {
        getSelectModel().setSelected(item, true);
    }

    /**
     * Adds an {@code ActionListener} to the {@code JORadioGroup}.
     * 
     * @param l the {@code ActionListener} to be added
     */
    public void addActionListener(ActionListener l) {
        listeners.add(ActionListener.class, l);
    }

    /**
     * Removes an {@code ActionListener} from the {@code JORadioGroup}.
     * 
     * @param l the {@code ActionListener} to be removed
     */
    public void removeActionListener(ActionListener l) {
        listeners.remove(ActionListener.class, l);
    }

    /**
     * Notifies all listeners that have registered interest for notification on this event type.<br/>
     * Updated all added views on the current {@code Model}. 
     * @see org.rlpw.swing.Model#updateViews
     */
    protected synchronized void fireActionEvent() {
        getDataModel().updateViews();
        ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "");
        for (ActionListener l : listeners.getListeners(ActionListener.class)) {
            l.actionPerformed(event);
        }
    }
}
